package luccy.json;

import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

import static luccy.json.JsonConstants.COLON;
import static luccy.json.JsonConstants.COMMA;
import static luccy.json.JsonConstants.EMPTY_JSON;
import static luccy.json.JsonConstants.END_BRACE;
import static luccy.json.JsonConstants.END_BRACKET;
import static luccy.json.JsonConstants.FALSE_STR;
import static luccy.json.JsonConstants.NULL_STR;
import static luccy.json.JsonConstants.SEMICOLON;
import static luccy.json.JsonConstants.START_BRACE;
import static luccy.json.JsonConstants.START_BRACKET;
import static luccy.json.JsonConstants.TRUE_STR;

/**
 * @author shot
 */
public class JsonUtil {

	private JsonUtil() {
	}
	
    public static String toJson(Object o) {
        StringBuilder buf = new StringBuilder(100);
        if(o instanceof String) {
        	return EMPTY_JSON;
        }
        append(buf, o);
        return buf.toString();
    }

    public static void append(StringBuilder buf, Object o) {
        if (o == null) {
            buf.append(NULL_STR);
        } else if (o instanceof String) {
            buf.append(quote((String) o));
        } else if (o instanceof Float) {
            appendFloat(buf, (Float) o);
        } else if (o instanceof Double) {
            appendDouble(buf, (Double) o);
        } else if (o instanceof Number) {
            buf.append(o.toString());
        } else if (o instanceof Boolean) {
            appendBoolean(buf, (Boolean) o);
        } else if (o instanceof Collection) {
            appendArray(buf, ((Collection) o).toArray());
        } else if (o instanceof Object[]) {
            appendArray(buf, (Object[]) o);
        } else if (o instanceof Map) {
            appendMap(buf, (Map) o);
        } else if (o.getClass().isArray()) {
            appendArray(buf, o);
        } else {
            //appendBean(buf, o);
        }
    }

    public static void appendFloat(StringBuilder buf, Float f) {
        if (f.isNaN() || f.isInfinite()) {
            throw new IllegalArgumentException(f.toString());
        }
        buf.append(f.toString());
    }

    public static void appendDouble(StringBuilder buf, Double d) {
        if (d.isNaN() || d.isInfinite()) {
            throw new IllegalArgumentException(d.toString());
        }
        buf.append(d.toString());
    }

    public static void appendBoolean(StringBuilder buf, Boolean b) {
        if (Boolean.TRUE.equals(b)) {
            buf.append(TRUE_STR);
        } else {
            buf.append(FALSE_STR);
        }
    }

    public static void appendArray(StringBuilder buf, Object[] array) {
        int length = array.length;
        buf.append(START_BRACKET);
        for (int i = 0; i < length; ++i) {
            append(buf, array[i]);
            buf.append(COMMA);
        }
        if (length > 0) {
            buf.setLength(buf.length() - 1);
        }
        buf.append(END_BRACKET);
    }
    
    public static void appendMap(StringBuilder buf, Map map) {
        buf.append(START_BRACE);
        for (Iterator i = map.keySet().iterator(); i.hasNext();) {
            String key = (String) i.next();
            buf.append(SEMICOLON);
            buf.append(key);
            buf.append(SEMICOLON);
            buf.append(COLON);
            append(buf, map.get(key));
            buf.append(COMMA);
        }
        if (map.size() > 0) {
            buf.setLength(buf.length() - 1);
        }
        buf.append(END_BRACE);
    }

    /*
    public static void appendBean(StringBuffer buf, Object bean) {
        buf.append(START_BRACE);
        BeanDesc beanDesc = BeanDescFactory.getBeanDesc(bean.getClass());
        for (int i = 0; i < beanDesc.getPropertyDescSize(); ++i) {
            PropertyDesc pd = beanDesc.getPropertyDesc(i);
            buf.append(pd.getPropertyName() + COLON);
            append(buf, pd.getValue(bean));
            buf.append(COMMA);
        }
        if (beanDesc.getPropertyDescSize() > 0) {
            buf.setLength(buf.length() - 1);
        }
        buf.append(END_BRACE);
    }
	*/

    public static void appendArray(StringBuilder buf, Object o) {
        int length = Array.getLength(o);
        buf.append(START_BRACKET);
        for (int i = 0; i < length; i++) {
            Object value = Array.get(o, i);
            append(buf, value);
            buf.append(COMMA);
        }
        if (length > 0) {
            buf.setLength(buf.length() - 1);
        }
        buf.append(END_BRACKET);
    }

    public static String quote(String str) {
        if (str == null || str.length() == 0) {
            return "\"\"";
        }
        char current = 0;
        int len = str.length();
        StringBuffer sb = new StringBuffer(len + 4);
        sb.append('"');
        for (int i = 0; i < len; ++i) {
            current = str.charAt(i);
            switch (current) {
            case '\\':
            case '/':
            case '"':
                sb.append('\\');
                sb.append(current);
                break;
            case '\b':
                sb.append("\\b");
                break;
            case '\t':
                sb.append("\\t");
                break;
            case '\n':
                sb.append("\\n");
                break;
            case '\f':
                sb.append("\\f");
                break;
            case '\r':
                sb.append("\\r");
                break;
            default:
                if (current < ' ') {
                    String t = "000" + Integer.toHexString(current);
                    sb.append("\\u" + t.substring(t.length() - 4));
                } else {
                    sb.append(current);
                }
            }
        }
        sb.append('"');
        return sb.toString();
    }
}
